package device

import (
	"context"
	"encoding/base64"
	"errors"
	"math/rand"
	"strconv"
	"sync"
	"time"

	"device-admin/config/redis"
	"device-admin/internal/app/grpc/protoc/devicec"
	"device-admin/internal/app/grpc/service/alarm_monitor"
	"device-admin/internal/app/grpc/service/pubsub"
	"device-admin/internal/app/grpc/service/robot"
	"device-admin/internal/app/grpc/service/terminal"
	"device-admin/internal/app/web/service/device"
	"device-admin/internal/define"

	"192.168.1.75/go-pkg/errorx"
	"192.168.1.75/go-pkg/go-cache"
	"192.168.1.75/go-pkg/logx"
	"github.com/robfig/cron/v3"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
	"google.golang.org/protobuf/proto"
)

type DeviceService struct {
	devicec.UnimplementedDeviceServiceServer
}

// getRobotID 获取元数据
func getRobotID(ctx context.Context) string {
	md, _ := metadata.FromIncomingContext(ctx)
	if len(md["clientid"]) > 0 {
		return md["clientid"][0]
	} else {
		return ""
	}
}

// web消息中转
var (
	WebMsgSend        sync.Map // web->grpcServer
	WebMsgReceive     sync.Map // grpcServer->web
	WebMsgReceiveLock sync.RWMutex
)

// MsgStreamWebShell
func (d *DeviceService) MsgStreamWebShell(ss devicec.DeviceService_MsgStreamWebShellServer) error {
	robotID := GetMetadataClientID(ss.Context())
	// 如webshell95
	terminalType := GetMetadataTerminalType(ss.Context())
	// metadata参数判断
	if robotID == "" {
		logx.Error(context.Background(), "MsgStreamWebShell robotID empty!")
		return errorx.New("robotID empty!")
	}
	if terminalType == "" {
		logx.Error(context.Background(), "MsgStreamWebShell terminalType empty!")
		return errorx.New("terminalType empty!")
	}
	// 退出日志
	defer func() {
		logx.Info(context.Background(), "MsgStreamWebShell exit", logx.String("robot_id", robotID))
	}()
	// 节点连接日志
	logx.Info(context.Background(), "node connected", logx.String("robotID", robotID), logx.String("terminalType", terminalType))
	// 获取对应终端类型的数据通道
	topic := terminalType + "@" + robotID
	// web终端发来的信息
	webMsgSendChanVal, _ := WebMsgSend.LoadOrStore(topic, make(chan *devicec.Msg, 100)) // 消息发送通道
	webMsgSendChan := webMsgSendChanVal.(chan *devicec.Msg)
	// 会话锁
	logx.Info(context.Background(), "MsgStreamShell95 wait lock", logx.String("robot_id", robotID))
	if !terminal.SessionLock(robotID, terminalType) {
		logx.Error(context.Background(), "repeated MsgStreamWebShell", logx.String("robot_id", robotID))
		return errorx.New("repeated MsgStreamWebShell", -1)
	}
	logx.Info(context.Background(), "MsgStreamShell95 get lock", logx.String("robot_id", robotID))
	defer terminal.SessionUnLock(robotID, terminalType)

	// 保存session的服务ip
	terminal.SaveSessionServerIP(robotID, terminalType)
	defer terminal.DelSessionServerIP(robotID, terminalType)

	// 自动添加终端
	terminal.AutoAddTerminal(robotID, terminalType)
	defer terminal.UpdateTerminalActiveTime(robotID, terminalType, time.Now().Unix()-60)

	// 最近ping的时间
	lastestPingTime := time.Now().Unix()

	// 会话连接上下文控制
	sessionCtx, sessionCancel := context.WithCancel(context.Background())
	defer sessionCancel()

	// msg发送channel
	sendMsgChan := make(chan *devicec.Msg, 100)

	// 心跳
	go func() {
		tk := time.NewTicker(time.Duration(time.Second * 10))
		defer tk.Stop()
		for {
			select {
			// 会话结束
			case <-ss.Context().Done():
				return
			case <-sessionCtx.Done():
				return
			// 每隔10秒定时发送心跳
			case <-tk.C:
				ping := &devicec.Ping{}
				msg := &devicec.Msg{}
				msg.Name = string(ping.ProtoReflect().Descriptor().FullName())
				msg.Content, _ = proto.Marshal(ping)
				pushToSendMsgChan(sendMsgChan, msg)
				// 客户端ping超时
				if time.Now().Unix()-lastestPingTime > 30 {
					sessionCancel()
					return
				}
				// 会话锁延期
				terminal.ExpireSessionLock(robotID, terminalType)
			}
		}
	}()

	// 接收节点的信息，转发给web终端
	go func() {
		for {
			select {
			case <-ss.Context().Done():
				return
			case <-sessionCtx.Done():
				return
			default:
				msg, err := ss.Recv()
				if err != nil {
					logx.Info(context.Background(), "receive msg from node error", logx.String("robotID", robotID))
					sessionCancel()
					return
				}
				switch msg.Name {
				// ping
				case string((&devicec.Ping{}).ProtoReflect().Descriptor().FullName()):
					lastestPingTime = time.Now().Unix()
					terminal.PushToActiveDeviceList(robotID, terminalType)
				// webshellCMD，web msg回复代理
				default:
					receiveTopic := msg.SessionId + "@" + terminalType + "@" + robotID
					WebMsgReceiveLock.Lock()
					if val, ok := WebMsgReceive.Load(receiveTopic); ok {
						webMsgReceiveChan := val.(chan *devicec.Msg)
						webMsgReceiveChan <- msg
					}
					WebMsgReceiveLock.Unlock()
				}
				time.Sleep(time.Millisecond)
			}
		}
	}()

	// 接收web终端的消息，发送给节点
	for {
		select {
		case <-ss.Context().Done():
			return nil
		case <-sessionCtx.Done():
			return nil
		// msg chan
		case msg := <-sendMsgChan:
			err := ss.Send(msg)
			if err != nil {
				logx.Info(context.Background(), "send msg to node error", logx.String("robotID", robotID))
				sessionCancel()
				return nil
			}
		// webMsgChan 发送代理
		case msg := <-webMsgSendChan:
			pushToSendMsgChan(sendMsgChan, msg)
		}
	}
}

// MsgStreamWeb web消息交互
func (d *DeviceService) MsgStreamWebMsgProxy(ss devicec.DeviceService_MsgStreamWebMsgProxyServer) error {
	robotID := GetMetadataClientID(ss.Context())
	terminalType := GetMetadataTerminalType(ss.Context())
	sessionID := GetMetadataSessionID(ss.Context())
	if robotID == "" {
		logx.Error(context.Background(), "MsgStreamWebShellProxy robotID empty!")
	}
	if terminalType == "" {
		logx.Error(context.Background(), "MsgStreamWebShellProxy terminalType empty!")
	}
	if sessionID == "" {
		logx.Error(context.Background(), "MsgStreamWebShellProxy sessionID empty!")
	}
	// 获取对应终端类型的数据通道
	sendTopic := terminalType + "@" + robotID
	// 向节点发送消息
	webMsgSendChanVal, _ := WebMsgSend.LoadOrStore(sendTopic, make(chan *devicec.Msg, 100)) // 消息发送通道
	webMsgSendChan := webMsgSendChanVal.(chan *devicec.Msg)
	// 接收节点的消息
	receiveTopic := sessionID + "@" + terminalType + "@" + robotID
	webMsgReceiveChanVal, _ := WebMsgReceive.LoadOrStore(receiveTopic, make(chan *devicec.Msg, 100)) // 消息接收通道
	webMsgReceiveChan := webMsgReceiveChanVal.(chan *devicec.Msg)
	// 退出时，删除map key
	defer func() {
		WebMsgReceiveLock.Lock()
		WebMsgReceive.Delete(receiveTopic)
		WebMsgReceiveLock.Unlock()
	}()
	// 最近ping的时间
	lastestPingTime := time.Now().Unix()
	// 会话连接上下文控制
	sessionCtx, sessionCancel := context.WithCancel(context.Background())
	defer sessionCancel()

	// 心跳
	go func() {
		tk := time.NewTicker(time.Duration(time.Second * 10))
		defer tk.Stop()
		for {
			select {
			// 会话结束
			case <-ss.Context().Done():
				return
			case <-sessionCtx.Done():
				return
			// 每隔10秒定时发送心跳
			case <-tk.C:
				ping := &devicec.Ping{}
				msg := &devicec.Msg{}
				msg.Name = string(ping.ProtoReflect().Descriptor().FullName())
				msg.Content, _ = proto.Marshal(ping)
				webMsgReceiveChan <- msg
				// ping 超时检测
				if time.Now().Unix()-lastestPingTime > 30 {
					sessionCancel()
					return
				}
			}
		}
	}()
	// 接收web客户端的消息
	// 接收web终端会话发来的消息，存储至webMsgSend
	go func() {
		for {
			select {
			case <-ss.Context().Done():
				return
			case <-sessionCtx.Done():
				return
			default:
				msg, err := ss.Recv()
				if err != nil {
					sessionCancel()
					return
				}
				// ping消息处理，其余放入channel
				if msg.Name == string((&devicec.Ping{}).ProtoReflect().Descriptor().FullName()) {
					lastestPingTime = time.Now().Unix()
				} else {
					msg.SessionId = sessionID
					pushToSendMsgChan(webMsgSendChan, msg)
				}
				time.Sleep(time.Millisecond)
			}
		}
	}()
	// 发送消息给web客户端
	for {
		select {
		case <-ss.Context().Done():
			return nil
		case <-sessionCtx.Done():
			return nil
		case msg := <-webMsgReceiveChan:
			ss.Send(msg)
		}
	}
}

// MsgStream97 97通用命令交互
func (d *DeviceService) MsgStream97(ss devicec.DeviceService_MsgStream97Server) error {
	return MsgStream(ss, "97")
}

// MsgStream98 98广告屏命令交互
func (d *DeviceService) MsgStream98(ss devicec.DeviceService_MsgStream98Server) error {
	return MsgStream(ss, "98")
}

type MsgStreamServer interface {
	Send(*devicec.Msg) error
	Recv() (*devicec.Msg, error)
	grpc.ServerStream
}

// pushToSendMsgChan
func pushToSendMsgChan(sendMsgChan chan *devicec.Msg, msg *devicec.Msg) {
	if len(sendMsgChan) < 100 {
		sendMsgChan <- msg
	} else {
		<-sendMsgChan
	}
}

// 消息流
func MsgStream(ss MsgStreamServer, terminalType string) error {
	robotID := getRobotID(ss.Context())
	if robotID == "" {
		logx.Error(context.Background(), "MsgStream"+terminalType, logx.String("error", "robot_id can not be empty"))
		return errors.New("robot_id can not be empty")
	}
	// 会话锁
	logx.Info(context.Background(), "MsgStream"+terminalType+" wait lock", logx.String("robot_id", robotID))
	if !terminal.SessionLock(robotID, terminalType) {
		logx.Error(context.Background(), "repeated MsgStream", logx.String("robot_id", robotID))
		return errorx.New("repeated MsgStream", -1)
	}
	logx.Info(context.Background(), "MsgStream"+terminalType+" get lock", logx.String("robot_id", robotID))
	defer terminal.SessionUnLock(robotID, terminalType)
	// 自动添加终端
	terminal.AutoAddTerminal(robotID, terminalType)
	defer terminal.UpdateTerminalActiveTime(robotID, terminalType, time.Now().Unix()-60)

	Init97Cron(robotID)
	// 移除定时器
	if terminalType == "97" {
		defer func() {
			if data, ok1 := Cron97IDs.Load(robotID); ok1 {
				if cronIDs, ok2 := data.([]cron.EntryID); ok2 {
					for _, cronID := range cronIDs {
						Cron97.Remove(cronID)
					}
				}
				Cron97IDs.Delete(robotID)
			}
		}()
	}
	// 主题订阅
	pubsub.AddSub(robotID, terminalType)

	// redis消息队列
	chanMapKey := "msg@" + robotID + "@" + terminalType
	pubsub.RobotCMDChanMap.Store(chanMapKey, make(chan string, 1))
	defer pubsub.RobotCMDChanMap.Delete(chanMapKey)
	robotCMDChanInterface, _ := pubsub.RobotCMDChanMap.Load(chanMapKey)
	CMDChan, _ := robotCMDChanInterface.(chan string)

	// 本地消息队列
	localMsgChanMapKey := "msg@" + robotID + "@" + terminalType
	pubsub.LocalMsgChannelMap.LoadOrStore(localMsgChanMapKey, make(chan *devicec.Msg, 50))
	localMsgChannelMap, _ := pubsub.LocalMsgChannelMap.Load(localMsgChanMapKey)
	localMsgChannel, _ := localMsgChannelMap.(chan *devicec.Msg)

	// 离线告警
	// defer terminal.AddOfflineAlarmCheck(robotID, terminalType)
	defer func() {
		alarmType := 0
		if terminalType == "97" {
			alarmType = 2002
		} else if terminalType == "98" {
			alarmType = 2003
		}
		alarm_monitor.AlarmMonitor{}.AddMonitor(robotID, alarmType, time.Now().Unix(), 1, false, "", "")
	}()
	terminal.RemoveOfflineAlarmCheck(robotID, terminalType)

	// 缓存实时状态
	func() {
		cache.HSet(define.CacheRobotRTInfo+robotID+":"+terminalType, "online", 1, 0, nil)
		cache.Expire(define.CacheRobotRTInfo+robotID+":"+terminalType, time.Second*30)
	}()

	// 会话结束控制context
	sessionCtx, sessionCancel := context.WithCancel(context.Background())
	defer sessionCancel()

	// 会话结束时，取消订阅
	defer func() {
		pubsub.CancelSub(robotID, terminalType)
	}()

	// 待发送的消息通道
	sendMsgChan := make(chan *devicec.Msg, 100)

	// 延迟发送广告计划
	rd := rand.New(rand.NewSource(time.Now().UnixNano()))
	time.AfterFunc(time.Second*time.Duration(rd.Intn(180)), func() {
		terminalTypeMap := map[string]string{
			"97": "1",
			"98": "2",
		}
		logx.Info(context.Background(), "GetPublishSchedule in MsgStream:"+robotID+"_"+terminalTypeMap[terminalType])
		terminal.GetPublishSchedule(robotID+"_"+terminalTypeMap[terminalType], true, "MsgStream")
	})

	// 心跳
	go func() {
		tk := time.NewTicker(time.Duration(time.Second * 10))
		defer tk.Stop()
		for {
			select {
			// 会话结束
			case <-ss.Context().Done():
				return
			case <-sessionCtx.Done():
				return
			// 每隔10秒定时发送心跳
			case <-tk.C:
				ping := &devicec.Ping{}
				msg := &devicec.Msg{}
				msg.Name = string(ping.ProtoReflect().Descriptor().FullName())
				msg.Content, _ = proto.Marshal(ping)
				pushToSendMsgChan(sendMsgChan, msg)
				// 会话锁延期
				terminal.ExpireSessionLock(robotID, terminalType)
			}
		}
	}()

	// 接收处理
	go func() {
		for {
			select {
			// 会话结束
			case <-ss.Context().Done():
				return
			case <-sessionCtx.Done():
				return
			default:
				msg, err := ss.Recv()
				// 消息处理
				if err == nil && msg != nil {
					// 发布消息响应
					PublishCMDRsp(ss, msg, terminalType)
					// 消息处理
					MsgHandler(ss, msg, terminalType)
					// 消息回应
					{
						ackMsg := AckMsgPack(msg)
						if ackMsg != nil {
							pushToSendMsgChan(sendMsgChan, ackMsg)
						}
					}
				}
				time.Sleep(time.Millisecond * 50)
			}
		}
	}()

	// 发送处理
	for {
		select {
		// 会话结束
		case <-ss.Context().Done():
			return nil
		case <-sessionCtx.Done():
			return nil
		// 消息串行发送
		case msg := <-sendMsgChan:
			if err := ss.Send(msg); err != nil {
				logx.Error(context.Background(), "MsgStream"+terminalType+" send error", logx.Err(err))
				sessionCancel()
				// 发送失败，则清除本地消息发送缓存,避免积压历史消息
				pubsub.LocalMsgChannelMap.Delete(localMsgChanMapKey)
				return nil
			}
		// 处理本地命令任务
		case msg := <-localMsgChannel:
			pushToSendMsgChan(sendMsgChan, msg)
		// 订阅到的命令下发
		case data := <-CMDChan:
			msg := &devicec.Msg{}
			dataBytes, _ := base64.StdEncoding.DecodeString(data)
			err := proto.Unmarshal(dataBytes, msg)
			if err == nil {
				pushToSendMsgChan(sendMsgChan, msg)
			}
		// 超过30秒接收不到设备状态数据，退出wildmsg
		default:
			if _, found := cache.HGet(define.CacheRobotRTInfo+robotID+":"+terminalType, "online"); !found {
				if terminalType == "97" {
					ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
					redis.NewDB().Set(ctx, define.RedisRobotRTInfo+robotID, "sale_status", 2) // 售卖状态
					cancel()
				}
				sessionCancel()
				return nil
			}
			time.Sleep(time.Millisecond * 50)
		}
	}
}

// GetMetadataClientID 从metadata中获取clientID
func GetMetadataClientID(ctx context.Context) string {
	md, _ := metadata.FromIncomingContext(ctx)
	if len(md["clientid"]) > 0 {
		return md["clientid"][0]
	} else {
		return ""
	}
}

// GetMetadataTerminalType 从metadata中获取终端类型
func GetMetadataTerminalType(ctx context.Context) string {
	md, _ := metadata.FromIncomingContext(ctx)
	if len(md["terminal_type"]) > 0 {
		return md["terminal_type"][0]
	} else if len(md["terminal-type"]) > 0 {
		return md["terminal-type"][0]
	} else {
		return ""
	}
}

// GetMetadataSessionID 从metadata中获取终端会话sessionID
func GetMetadataSessionID(ctx context.Context) string {
	md, _ := metadata.FromIncomingContext(ctx)
	if len(md["session_id"]) > 0 {
		return md["session_id"][0]
	} else if len(md["session-id"]) > 0 {
		return md["session-id"][0]
	} else {
		return ""
	}
}

// DeviceList 设备列表
func (d *DeviceService) DeviceListAd(ctx context.Context, req *devicec.DeviceListAdReq) (*devicec.DeviceListAdRsp, error) {
	res := &devicec.DeviceListAdRsp{}

	// Convert request parameters
	online := -1
	if req.Online != 0 {
		online = int(req.Online)
	}

	// Get device list using the existing service function
	list, total, err := device.DeviceList(
		req.MerchantId,
		req.MerchantPath,
		req.RobotId,
		req.DeviceTypes,
		req.MerchantIds,
		online,
		int(req.Page),
		int(req.Limit),
		nil,
		req.ProvinceId,
		req.CityId,
		req.DistrictId,
		req.LocationId,
		int(req.SubMode),
		req.Longitude,
		req.Latitude,
	)
	if err != nil {
		return res, err
	}

	// Convert device list to response format
	devices := make([]*devicec.DeviceAd, 0, len(list))
	for _, item := range list {
		deviceWithLocation, total := device.GetDeviceWithLocation(item.RobotID, "", "", "", "", "", 0, 0, "", "", 0)
		var OpeningHours string = ""
		if total > 0 {
			OpeningHours = deviceWithLocation[0].OpeningHours
		}
		device := &devicec.DeviceAd{
			Id:                 item.ID,
			RobotId:            item.RobotID,
			MacAddr:            item.MacAddr,
			MerchantId:         item.MerchantID,
			MerchantPath:       item.MerchantPath,
			IdentificationCode: item.IdentificationCode,
			DeviceType:         int32(item.DeviceType),
			DeviceIp:           item.DeviceIP,
			LocationId:         item.LocationID,
			FloorId:            item.FloorID,
			PositionId:         item.PositionID,
			CurrentMapName:     item.CurrentMapName,
			LatestActiveTime:   item.LatestActiveTime,
			ServicePhone:       item.ServicePhone,
			FirmwareVersion:    item.FirmwareVersion,
			Battery:            int32(item.Battery),
			CreatedAt:          item.CreatedAt,
			UpdatedAt:          item.UpdatedAt,
			DeviceTypeTitle:    item.DeviceTypeTitle,
			Online:             int32(item.Online),
			LocationName:       item.LocationName,
			LocationLatitude:   item.LocationLatitude,
			LocationLongitude:  item.LocationLongitude,
			FloorName:          item.FloorName,
			MerchantName:       item.MerchantName,
			Mode:               int32(item.Mode),
			ModeName:           item.ModeName,
			SubMode:            int32(item.SubMode),
			SubModeName:        item.SubModeName,
			TaskDataUpdatedAt:  item.TaskDataUpdatedAt,
			Addr:               item.Addr,
			PositionName:       item.PositionName,
			DistKm:             item.DistKM,
			OpeningHours:       OpeningHours,
			Images:             item.Images,
		}
		devices = append(devices, device)
	}

	res.Total = total
	res.Devices = devices
	return res, nil
}

// AllDevice 所有设备
func (d *DeviceService) AllDeviceAd(ctx context.Context, req *devicec.DeviceListAdReq) (*devicec.DeviceListAdRsp, error) {
	res := &devicec.DeviceListAdRsp{}

	// Convert request parameters
	online := -1
	if req.Online != 0 {
		online = int(req.Online)
	}

	// Get device list using the existing service function
	list, total, err := device.DeviceList(
		req.MerchantId,
		req.MerchantPath,
		req.RobotId,
		req.DeviceTypes,
		req.MerchantIds,
		online,
		int(req.Page),
		int(req.Limit),
		nil,
		req.ProvinceId,
		req.CityId,
		req.DistrictId,
		req.LocationId,
		int(req.SubMode),
		req.Longitude,
		req.Latitude,
	)
	if err != nil {
		return res, err
	}

	// Convert device list to response format
	devices := make([]*devicec.DeviceAd, 0, len(list))
	for _, item := range list {

		deviceWithLocation, total := device.GetDeviceWithLocation(item.RobotID, "", "", "", "", "", 0, 0, "", "", 0)
		var OpeningHours string = ""
		if total > 0 {
			OpeningHours = deviceWithLocation[0].OpeningHours
		}
		device := &devicec.DeviceAd{
			Id:                 item.ID,
			RobotId:            item.RobotID,
			MacAddr:            item.MacAddr,
			MerchantId:         item.MerchantID,
			MerchantPath:       item.MerchantPath,
			IdentificationCode: item.IdentificationCode,
			DeviceType:         int32(item.DeviceType),
			DeviceIp:           item.DeviceIP,
			LocationId:         item.LocationID,
			FloorId:            item.FloorID,
			PositionId:         item.PositionID,
			CurrentMapName:     item.CurrentMapName,
			LatestActiveTime:   item.LatestActiveTime,
			ServicePhone:       item.ServicePhone,
			FirmwareVersion:    item.FirmwareVersion,
			Battery:            int32(item.Battery),
			CreatedAt:          item.CreatedAt,
			UpdatedAt:          item.UpdatedAt,
			DeviceTypeTitle:    item.DeviceTypeTitle,
			Online:             int32(item.Online),
			LocationName:       item.LocationName,
			LocationLatitude:   item.LocationLatitude,
			LocationLongitude:  item.LocationLongitude,
			FloorName:          item.FloorName,
			MerchantName:       item.MerchantName,
			Mode:               int32(item.Mode),
			ModeName:           item.ModeName,
			SubMode:            int32(item.SubMode),
			SubModeName:        item.SubModeName,
			TaskDataUpdatedAt:  item.TaskDataUpdatedAt,
			Addr:               item.Addr,
			PositionName:       item.PositionName,
			DistKm:             item.DistKM,
			OpeningHours:       OpeningHours,
			Images:             item.Images,
		}
		devices = append(devices, device)
	}

	res.Total = total
	res.Devices = devices
	return res, nil
}

// DeviceDetail 设备详情
func (d *DeviceService) DeviceDetailAd(ctx context.Context, req *devicec.DeviceListAdReq) (*devicec.DeviceListAdRsp, error) {
	res := &devicec.DeviceListAdRsp{}

	// Get device list using the existing service function with page=1, limit=1
	list, total, err := device.DeviceList(
		req.MerchantId,
		req.MerchantPath,
		req.RobotId,
		"",
		"",
		-1,
		1,
		1,
		nil,
		"", "", "", "",
		0, 0, 0,
	)
	if err != nil {
		return res, err
	}

	if total <= 0 {
		return res, errorx.New("设备不存在", -1)
	}
	deviceWithLocation, total := device.GetDeviceWithLocation(list[0].RobotID, "", "", "", "", "", 0, 0, "", "", 0)
	var OpeningHours string = ""
	if total > 0 {
		OpeningHours = deviceWithLocation[0].OpeningHours
	}
	// Convert device to response format
	device := &devicec.DeviceAd{
		Id:                 list[0].ID,
		RobotId:            list[0].RobotID,
		MacAddr:            list[0].MacAddr,
		MerchantId:         list[0].MerchantID,
		MerchantPath:       list[0].MerchantPath,
		IdentificationCode: list[0].IdentificationCode,
		DeviceType:         int32(list[0].DeviceType),
		DeviceIp:           list[0].DeviceIP,
		LocationId:         list[0].LocationID,
		FloorId:            list[0].FloorID,
		PositionId:         list[0].PositionID,
		CurrentMapName:     list[0].CurrentMapName,
		LatestActiveTime:   list[0].LatestActiveTime,
		ServicePhone:       list[0].ServicePhone,
		FirmwareVersion:    list[0].FirmwareVersion,
		Battery:            int32(list[0].Battery),
		CreatedAt:          list[0].CreatedAt,
		UpdatedAt:          list[0].UpdatedAt,
		DeviceTypeTitle:    list[0].DeviceTypeTitle,
		Online:             int32(list[0].Online),
		LocationName:       list[0].LocationName,
		LocationLatitude:   list[0].LocationLatitude,
		LocationLongitude:  list[0].LocationLongitude,
		FloorName:          list[0].FloorName,
		MerchantName:       list[0].MerchantName,
		Mode:               int32(list[0].Mode),
		ModeName:           list[0].ModeName,
		SubMode:            int32(list[0].SubMode),
		SubModeName:        list[0].SubModeName,
		TaskDataUpdatedAt:  list[0].TaskDataUpdatedAt,
		Addr:               list[0].Addr,
		PositionName:       list[0].PositionName,
		DistKm:             list[0].DistKM,
		OpeningHours:       OpeningHours,
		Images:             list[0].Images,
	}

	// If device is online, get realtime info
	if list[0].Online == 1 {
		rtInfo := robot.GetRealtimeInfo(list[0].RobotID)
		// Convert rtInfo map to device fields
		if rtInfo != nil {
			if v, ok := rtInfo["battery"]; ok {
				if batteryStr, ok := v.(string); ok {
					if battery, err := strconv.Atoi(batteryStr); err == nil {
						device.Battery = int32(battery)
					}
				}
			}
			if v, ok := rtInfo["mode"]; ok {
				if modeStr, ok := v.(string); ok {
					if mode, err := strconv.Atoi(modeStr); err == nil {
						device.Mode = int32(mode)
					}
				}
			}
			if v, ok := rtInfo["sub_mode"]; ok {
				if subModeStr, ok := v.(string); ok {
					if subMode, err := strconv.Atoi(subModeStr); err == nil {
						device.SubMode = int32(subMode)
					}
				}
			}
			// Add other realtime info fields as needed
		}
	}

	res.Total = 1
	res.Devices = []*devicec.DeviceAd{device}
	return res, nil
}
